﻿using System;
using System.Text;
using System.Security.Cryptography;

namespace Co9320.NamaAlert.Shared
{
	public class TokenProcessor:IDisposable
	{
		private const string Prefix = "SIGNATURE";
		private const int KeyLength = 16;
		private const int TokenLength = 32;
		private static readonly DateTime TheEpoch = new DateTime(1970,01,01,00,00,00, DateTimeKind.Utc);
		private const long ConversionParameter = 10000000;

		private readonly byte[] Key;
		private readonly HMACSHA256 TokenHasher;

		public TokenProcessor(string ApplicationId, string KeySeed)
		{
			System.Diagnostics.Debug.Assert(ApplicationId != null);
			System.Diagnostics.Debug.Assert(KeySeed != null);

			this.ApplicationId = ApplicationId;
			this.KeySeed = KeySeed;

			this.Key = GetKey(this.KeySeed);

			TokenHasher = new HMACSHA256(this.Key);
		}

		public string KeySeed
		{
			get;
			private set;
		}

		public string ApplicationId
		{
			get;
			private set;
		}

		public byte[] GetKey()
		{
			return GetKey(this.KeySeed);
		}


		private static byte[] GetKey(string KeySeed)
		{
			if (KeySeed == null)
			{
				KeySeed = "";
			}

			byte[] Key = new byte[KeyLength];

			System.Diagnostics.Trace.Assert(Key.Length == KeyLength);

			byte[] enc = Encoding.Default.GetBytes(Prefix + KeySeed);
			byte[] dta = null;

			using (SHA256 hasher = SHA256.Create())
			{
				dta = hasher.ComputeHash(enc);
			}

			for (int i = 0; i < KeyLength; i++)
			{
				Key[i] = dta[i];
			}

			return Key;
		}

		public static long GetTime()
		{
			long tmp = DateTime.Now.Ticks;
			tmp -= TheEpoch.Ticks;

			return tmp / ConversionParameter;
		}

		public string GetToken(long Time)
		{
			string HashSource = GetHashSource(Time);
			return ComputeHash(HashSource);
		}

		public bool Inspect(long Time, string HashValue)
		{
			string Expected = GetToken(Time);

			if (HashValue == null)
			{
				return false;
			}

			return Expected == HashValue;
		}

		private string GetHashSource(long Time)
		{
			return string.Format("appid={0} & ts={1}", this.ApplicationId, Time);
		}

		private string ComputeHash(string HashSource)
		{
			byte[] Buffer = Encoding.Unicode.GetBytes(HashSource);

			Buffer = TokenHasher.ComputeHash(Buffer);

			return System.Convert.ToBase64String(Buffer);
		}

		#region IDisposable メンバ

		public void Dispose()
		{
			if (TokenHasher != null)
			{
				TokenHasher.Clear();
			}
		}

		#endregion
	}
}